<template>
  <div class="audition6">
    <h3>什么是深拷贝浅拷贝？</h3>
    <hr>
    <h3>在此之前，先说说什么是数据存储？</h3>
    <p>这要借助于堆栈内存的概念来理解</p>
    <p>基础数据类型的值、<span class="color">复杂数据类型中的函数</span>和复杂数据类型的引用，都存放于栈内存中</p>
    <p>复杂数据类型的值(函数除外)，则存放于堆内存中</p>
    <p>举个例子：</p>
    <code class="code">
      var a = 1
    </code>
    <p>当创建一个变量时，会在栈内存中开辟一个空间，名字就叫<span class="color">a</span></p>
    <p>当给这个变量赋值一个基础数据类型的值时，这个值就存在于<span class="color">a</span>空间中，这个空间的值就是<span class="color">1</span></p>
    <img src="@images/zhan1.png" alt="" class="img" />
    <p>当给这个变量赋值一个复杂数据类型的值时，同样会在栈内存中开辟一个空间，这个空间中存放了一个指向堆内存的值的引用</p>
    <p>但是会把值存放在堆内存中</p>
    <p>举个例子：</p>
    <code class="code">
      var obj = {b: 1}
    </code>
    <p>此时，表现如下：</p>
    <img src="@images/zhan2.png" alt="" class="img" />
    <p>存放于栈内存中的<span class="color">obj</span>，通过<span class="color">→</span>引用，指向了存放于堆内存里的复杂数据类型的值<span class="color">{b: 1}</span>。</p>
    <hr>
    <h3>再来说说啥是深拷贝浅拷贝</h3>
    <p>对于基础数据类型的数据来说，拷贝就是赋值，因为同在栈内存中，只是空间不同。</p>
    <p>比如，</p>
    <code class="code">
      var a = 1<br />
      var b = a<br />
    </code>
    <p>此时在栈内存开辟了<span class="color">a</span>和<span class="color">b</span>两个空间</p>
    <p>a的值为1，当执行<span class="color">b = a</span>时，b空间的值也是1</p>
    <p>如图：</p>
    <img src="@images/zhan3.png" alt="" class="img" />
    <p>此时，两个空间是独立的，修改a的值，或者修改b的值，都不会引起对方的值的变化</p>
    <code class="code">
      var a = 1<br />
      var b = a<br />
      b = 2<br />
      console.log(a, b)
    </code>
    <p>输出结果为：</p>
    <code class="code">
      1&nbsp;2
    </code>
    <p>这种拷贝，就可以称为<span class="color">深拷贝</span></p>
    <p>再以复杂数据类型为例：</p>
    <code class="code">
      var obj1 = {a: 1}<br />
      var obj2 = obj1<br />
      obj2.a = 2<br />
      console.log(obj1.a, obj2.a)
    </code>
    <p>输出结果为：</p>
    <code class="code">
      2&nbsp;2
    </code>
    <p>可以看到，修改其中一个变量的值，另一个变量的值也跟着变了。这种拷贝就叫做<span class="color">浅拷贝</span></p>
    <p>分析这种拷贝产生的原因：</p>
    <p>声明变量obj1，它的值是一个复杂数据类型，所以此时，在栈内存中开辟一个空间，就叫obj1</p>
    <p>同时，把指向堆内存的值的引用(也叫指针)存放到该空间中</p>
    <p>在堆内存也开辟了一个空间，用于存放obj1的值<span class="color">{a: 1}</span></p>
    <p>如图：</p>
    <img src="@images/zhan4.png" alt="" class="img" />
    <p>当执行<span class="color">var obj2 = obj1</span>时</p>
    <p>会在栈内存中开辟一个空间，叫obj2。这时候的赋值操作，是<span class="color">把obj1的引用交给obj2一份</span></p>
    <p>于是，obj2就和obj1拥有了同一份引用，都指向了<span class="color">{a: 1}</span></p>
    <p>如图所示：</p>
    <img src="@images/zhan5.png" alt="" class="img" />
    <p>此时再去修改obj1或者obj2的值，都是修改了{a: 1}</p>
    <p>而引用还是那个引用，指向的位置并没有变化，所以，另一个变量，总是跟着变的</p>
    <p>至此，应该基本弄清楚了深拷贝浅拷贝的概念</p>
    <hr>
    <h3>现在，来看下复杂数据类型实现浅拷贝都有哪些方法</h3>
    <p>1. 直接用<span class="color">=</span>赋值</p>
    <p>如上面举出的obj1 obj2赋值例子</p>
    <hr>
    <h3>复杂数据类型实现深拷贝的方法</h3>
    <p>1. 使用JSON对象方法</p>
    <code class="code">
      var obj1 = {a: 1}<br />
      var obj2 = JSON.parse(JSON.stringify(obj1))<br />
      obj2.a = 2<br />
      console.log(obj1.a, obj2.a) // 1 &nbsp; 2
    </code>
    <p>这种常用于session对象存取</p>
    <p>缺点：</p>
    <p>&emsp;如果对象中有函数，函数不能拷贝下来</p>
    <p>&emsp;无法拷贝对象原型链上的属性和方法</p>
    <p>&emsp;当数据的层次很深，会栈溢出</p>
    <p>2. 递归</p>
    <v-md-preview :text="text" />
    <p>3. jquery中的extend方法</p>
    <code class="code">
      var arr1 = [1, 2, 3, 4]<br />
      var arr2 = $.extend(true, [], arr1) // true深拷贝 false浅拷贝<br />
      arr2[1] = 'a'<br />
      console.log(arr2, arr1)
    </code>
    <p>4. lodash函数库</p>
    <code class="code">
      var result = _.cloneDeep(test)
    </code>
    <p>5. 手动实现</p>
    <code class="code">
      var obj1 = {a: 1}<br />
      var obj2 = {a: obj1.a}<br />
      obj2.a = 3<br />
      console.log(obj1, obj2)
    </code>
    <p>6. concat实现数组的深拷贝</p>
    <code class="code">
      var arr1 = [1, 2, 3]<br />
      var arr2 = arr1.concat()<br />
      arr2[1] = 'a'<br />
      console.log(arr1, arr2)
    </code>
    <p>7. slice实现数组的深拷贝</p>
    <code class="code">
      var arr1 = [1, 2, 3]<br />
      var arr2 = arr1.slice()<br />
      arr2[1] = 'a'<br />
      console.log(arr1, arr2)
    </code>
    <p>8. Object.assign(注意写法)</p>
    <code class="code">
      var obj1 = {a: 1}<br />
      var obj2 = Object.assign({}, obj1)<br />
      obj2.a = 'a'<br />
      console.log(obj1, obj2)
    </code>
  </div>
</template>

<script>
const prefix = '``` js'
const suffix = '```'
export default {
  name: 'Audition6',
  data() {
    return {
      text: `${prefix}  
      var deepCopy = function(obj) {
        if(typeof obj !== 'object') return obj
        var _obj = Array.isArray(obj) ? [] : {}
        for (var k in obj) {
            if (typeof obj[k] === 'object') {
                _obj[k] = deepCopy(obj[k])
            } else {
                _obj[k] = obj[k]
            }
        }
        return _obj
      }
      var obj1 = {
          a: [
              {
                  b: 1,
                  c: function() {console.log('c')}
              },
              [2, {d: 3}]
          ],
          e: function() {console.log('e')},
          f: 4
      }
      var obj2 = deepCopy(obj1)
      obj2.a[0].b = 'b'
      console.log(obj2, obj1.a[0].b, obj2.a[0].b)
      \n${suffix}`
    }
  }
}
</script>

<style lang="less" scoped>

</style>